package org.codehaus.activemq.service.impl;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Iterator;
import javax.transaction.xa.XAException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.activemq.broker.Broker;
import org.codehaus.activemq.service.Transaction;
import org.codehaus.activemq.service.TransactionTask;

public abstract class AbstractTransaction
  implements Transaction, Externalizable
{
  private static final Log log = LogFactory.getLog(AbstractTransaction.class);
  public static final byte START_STATE = 0;
  public static final byte IN_USE_STATE = 1;
  public static final byte PREPARED_STATE = 2;
  public static final byte FINISHED_STATE = 3;
  private ArrayList prePrepareTasks = new ArrayList();
  private ArrayList postCommitTasks = new ArrayList();
  private ArrayList postRollbackTasks = new ArrayList();
  private byte state = 0;
  private transient Broker broker;

  protected AbstractTransaction(Broker broker)
  {
    this.broker = broker;
  }

  public Broker getBroker() {
    return this.broker;
  }

  public void setBroker(Broker broker)
  {
    this.broker = broker;
  }

  public byte getState() {
    return this.state;
  }

  public void setState(byte state) {
    this.state = state;
  }

  public void addPostCommitTask(TransactionTask r) {
    this.postCommitTasks.add(r);
    if (this.state == 0)
      this.state = 1;
  }

  public void addPostRollbackTask(TransactionTask r)
  {
    this.postRollbackTasks.add(r);
    if (this.state == 0)
      this.state = 1;
  }

  public void addPrePrepareTask(TransactionTask r)
  {
    this.prePrepareTasks.add(r);
    if (this.state == 0)
      this.state = 1;
  }

  public void prePrepare()
    throws Throwable
  {
    switch (this.state) {
    case 0:
    case 1:
      break;
    default:
      XAException xae = new XAException("Prepare cannot be called now.");
      xae.errorCode = -6;
      throw xae;
    }

    for (Iterator iter = this.prePrepareTasks.iterator(); iter.hasNext(); ) {
      TransactionTask r = (TransactionTask)iter.next();
      r.execute(this.broker);
    }
  }

  protected void postCommit() throws Throwable
  {
    for (Iterator iter = this.postCommitTasks.iterator(); iter.hasNext(); ) {
      TransactionTask r = (TransactionTask)iter.next();
      r.execute(this.broker);
    }
  }

  public void postRollback() throws Throwable
  {
    for (Iterator iter = this.postRollbackTasks.iterator(); iter.hasNext(); ) {
      TransactionTask r = (TransactionTask)iter.next();
      r.execute(this.broker);
    }
  }

  public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    this.state = in.readByte();
    this.prePrepareTasks = readTaskList(in);
    this.postCommitTasks = readTaskList(in);
    this.postRollbackTasks = readTaskList(in);
  }

  public void writeExternal(ObjectOutput out) throws IOException {
    out.writeByte(this.state);
    writeTaskList(this.prePrepareTasks, out);
    writeTaskList(this.postCommitTasks, out);
    writeTaskList(this.postRollbackTasks, out);
  }

  public String toString() {
    return super.toString() + "[prePrepares=" + this.prePrepareTasks + "; postCommits=" + this.postCommitTasks + "; postRollbacks=" + this.postRollbackTasks + "]";
  }

  protected ArrayList readTaskList(ObjectInput in)
    throws IOException
  {
    int size = in.readInt();
    ArrayList answer = new ArrayList(size);
    for (int i = 0; i < size; i++) {
      answer.add(readTask(in));
    }
    return answer;
  }

  protected void writeTaskList(ArrayList tasks, ObjectOutput out) throws IOException {
    int size = tasks.size();
    out.writeInt(size);
    for (int i = 0; i < size; i++)
      writeTask((TransactionTask)tasks.get(i), out);
  }

  protected TransactionTask readTask(ObjectInput in) throws IOException
  {
    return PacketTransactionTask.readTask(in);
  }

  protected void writeTask(TransactionTask task, ObjectOutput out) throws IOException {
    PacketTransactionTask.writeTask(task, out);
  }
}